package com.stone.serviceimpl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileFilter;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import com.stone.service.ILoggerService;
import com.stone.utils.LogConstrants;

public class LoggerService implements ILoggerService {
//	public static int STONE_ADD = 1;
//	public static int STONE_UPDATE = 2;
//	public static int PROVIDER_ADD = 3;
//	public static int PROVIDER_UPDATE = 4;
//	public static int[] LOG_TYPE = {STONE_ADD, STONE_UPDATE, PROVIDER_ADD, PROVIDER_UPDATE};
//	public static String[] LOG_INFO = {"石材添加", "石材更新", "供应商添加", "供应商更新"};
	
	private static int YEAR = 1;
	private static int MONTH = 2;
	private static int DAY = 3;
	
	private static String LOG_ROOT = "d:/log_dir";
	private static String LOG_WEEK = "week";
	private static String LINE = System.getProperty("line.separator");
	private static String SEPERATE = " ";
	private static String STAT_SEPERATE = "  ";
	private static String FIELD_SEPERATE = ":";
	
	private String startFileName = null;
	private String endFileName = null;

	private static FileFilter filter = new FileFilter() {
		public boolean accept(File f) {
			return f.isFile();
		}
	};
	
	private SimpleDateFormat descTimeFormat = new SimpleDateFormat("[yyyy-MM-dd HH:mm:ss]: ");
	private SimpleDateFormat weekFileFormat = new SimpleDateFormat("MM-dd");
	
	public void log(String user, String msg, int msgType) {
		FileWriter writer = null;
		try {
			writer = new FileWriter(_getLogFilePath(user), true);
			writer.append(_getFormatMsg(msg, msgType));
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			if(writer != null)
				try {
					writer.close();
				} catch (IOException e) {
					e.printStackTrace();
				}
		}
	}
	
	public void dayStatistic() {
		dayStatistic(new Date());
	}
	
	public void dayStatistic(Date date) {
		//放到月目录下
		String destFile = _getPath(MONTH, date) + "/" + _getStatisticFileName(DAY, date);
		String fromDir = _getPath(DAY);
		_statLog(fromDir, destFile, filter);
	}
	
	public void weekStatistic() {
		weekStatistic(new Date());
	}
	
	public void weekStatistic(Date date) {
		//放到年目录下
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		int endDay = cal.get(Calendar.DAY_OF_MONTH);
		Date startDate = new Date(date.getTime() - (6 * 24 * 60 * 60 * 1000));
		startFileName = _getStatisticFileName(DAY, startDate);
		endFileName = _getStatisticFileName(DAY, date);
		
		String weekDir = _getPath(YEAR, date) + "/" + LOG_WEEK;
		_checkDir(weekDir);
		String destFile = _getWeekFilePath(startDate, date);
		String fromDir = _getPath(MONTH, date);
		
		FileFilter filter = new FileFilter() {
			public boolean accept(File f) {
				return f.getName().compareTo(LoggerService.this.startFileName) >= 0 
					&& f.getName().compareTo(LoggerService.this.endFileName) <= 0;
			}
		};
		
		_statistic(fromDir, destFile, filter, false);
		
		//跨月的情况
		if(endDay < 7) {
			fromDir = _getPath(MONTH, startDate);
			_statistic(fromDir, destFile, filter, true);
			_mergeRecordOfOneFile(destFile);
		}
	}
	
	public void monthStatistic() {
		monthStatistic(new Date());
	}
	
	public void monthStatistic(Date date) {
		//放到年目录下
		String destFile = _getPath(YEAR, date) + "/" + _getStatisticFileName(MONTH, date);
		String fromDir = _getPath(MONTH);
		_statistic(fromDir, destFile, filter, false);
	}
	
	public void yearStatistic() {
		yearStatistic(new Date()); 
	}
	
	public void yearStatistic(Date date) {
		//放到根目录下
		String destFile = LOG_ROOT + "/" + _getStatisticFileName(YEAR);
		String fromDir = _getPath(YEAR, date);
		_statistic(fromDir, destFile, filter, false);
	}
	
	private void _statLog(String fromDir, String destFile, FileFilter filter) {
		FileInputStream fis = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		String line = null;
		try {
			File dir = new File(fromDir);
			if(! dir.exists() || ! dir.isDirectory()) {
				System.out.println(fromDir + "不存在这样的目录");
				return;
			}
			
			File[] subFiles = dir.listFiles(filter);
			Map<Integer, Integer> map = new HashMap<Integer, Integer>();
			int index = -1;
			Integer count = 0;
			FileWriter writer = new FileWriter(destFile);
			
			for(File file : subFiles) {
				//open
				fis = new FileInputStream(file);
				isr = new InputStreamReader(fis);
				br = new BufferedReader(isr);
				//read
				while((line = br.readLine()) != null) {
					index = line.indexOf(SEPERATE);
					if(index == -1)
						continue;
					int msgType = Integer.parseInt(line.substring(0, index));
					count = map.get(msgType);
					if(count == null)
						count = 0;
					map.put(msgType, ++count);
				}
				//write a record
				_writeRecord(writer, file.getName().substring(0, file.getName().lastIndexOf(".")), map);
				//close
				br.close();
				isr.close();
				fis.close();
				map.clear();
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void _statistic(String fromDir, String destFile, FileFilter filter, boolean append) {
		FileInputStream fis = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		String line = null;
		try {
			File dir = new File(fromDir);
			if(! dir.exists() || ! dir.isDirectory()) {
				System.out.println(fromDir + "不存在这样的目录");
				return;
			}
			
			File[] subFiles = dir.listFiles(filter);
			Map<String, Map<Integer, Integer>> map = new HashMap<String, Map<Integer, Integer>>();
			FileWriter writer = new FileWriter(destFile, append);
			
			for(File file : subFiles) {
				//open
				fis = new FileInputStream(file);
				isr = new InputStreamReader(fis);
				br = new BufferedReader(isr);
				String[] record = null;
				//read
				while((line = br.readLine()) != null) {
					record = line.split(STAT_SEPERATE);
					_parseRecord(map, record);
				}
				//close
				br.close();
				isr.close();
				fis.close();
			}
			//write
			for(String user : map.keySet()) {
				_writeRecord(writer, user, map.get(user));
			}
			writer.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void _mergeRecordOfOneFile(String filePath) {
		if(! new File(filePath).exists()) 
			return;
		String newFile = filePath + "_new";
		FileInputStream fis = null;
		InputStreamReader isr = null;
		BufferedReader br = null;
		String line = null;
		Map<String, Map<Integer, Integer>> map = new HashMap<String, Map<Integer, Integer>>();
		//open
		try {
			FileWriter writer = new FileWriter(newFile);
			fis = new FileInputStream(new File(filePath));
			isr = new InputStreamReader(fis);
			br = new BufferedReader(isr);
			String[] record = null;
			//read
			while((line = br.readLine()) != null) {
				record = line.split(STAT_SEPERATE);
				_parseRecord(map, record);
			}
			//write
			for(String user : map.keySet()) {
				_writeRecord(writer, user, map.get(user));
			}
			//close
			br.close();
			isr.close();
			fis.close();
			writer.close();
			new File(filePath).delete();
			new File(newFile).renameTo(new File(filePath));
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	private void _writeRecord(FileWriter writer, String user, Map<Integer, Integer> map) throws IOException {
		writer.append(user + STAT_SEPERATE);
		for(int i=0; i<LogConstrants.LOG_TYPE.length; i++) {
			writer.append(LogConstrants.LOG_INFO[i] + FIELD_SEPERATE + (map.get(LogConstrants.LOG_TYPE[i]) == null? 0 : map.get(LogConstrants.LOG_TYPE[i])) + STAT_SEPERATE);
		}
		writer.append(LINE);
	}
	
	private void _parseRecord(Map<String, Map<Integer, Integer>> map, String[] record) {
		Map<Integer, Integer> tmpMap = map.get(record[0]);
		if(tmpMap == null)
			tmpMap = new HashMap<Integer, Integer>();
		
		String[] field = null;
		Integer count = 0;
		for(int i=1; i<record.length; i++) {
			field = record[i].split(FIELD_SEPERATE);
			count = tmpMap.get(LogConstrants.LOG_TYPE[i-1]);
			if(count == null)
				count = 0;
			count += Integer.parseInt(field[1]);
			tmpMap.put(LogConstrants.LOG_TYPE[i-1], count);
		}
		map.put(record[0], tmpMap);
	}
	
	private String _getLogFilePath(String user) throws IOException {
		return _getLogFilePath(user, new Date());
	}
	
	private String _getLogFilePath(String user, Date date) throws IOException {
		String dirPath = _getPath(DAY);
		String filePath = dirPath + "/" + user + ".txt";
		_checkDir(dirPath);
		_checkFile(filePath);
		return filePath;
	}
	
	private String _getFormatMsg(String msg, int msgType) {
		Date now = new Date();
		String descTime = descTimeFormat.format(now);
		return msgType + SEPERATE + descTime + msg + LINE;
	}
	
	private String _getStatisticFileName(int type) {
		return _getStatisticFileName(type, new Date());
	}
	
	private String _getWeekFilePath(Date startDate, Date endDate) {
		String weekDir = _getPath(YEAR, endDate) + "/" + LOG_WEEK;
		_checkDir(weekDir);
		return weekDir + "/" + weekFileFormat.format(startDate) + "_" + weekFileFormat.format(endDate) + ".txt";
	}
	
	private String _getStatisticFileName(int type, Date date) {
		String fileName = null;
		switch(type) {
			case 1:
				fileName = "yyyy";
				break;
			case 2:
				fileName = "yyyy-MM";
				break;
			case 3:
				fileName = "yyyy-MM-dd";
				break;
			default:
				fileName = "yyyy-MM-dd";
		}
		
		SimpleDateFormat dateFormat = new SimpleDateFormat(fileName);
		return dateFormat.format(date) + ".txt";
	}
	
	private String _getPath(int type) {
		return _getPath(type, new Date());
	}
	
	private String _getPath(int type, Date date) {
		String format = null;
		switch(type) {
			case 1:
				format = "yyyy";
				break;
			case 2:
				format = "yyyy/MM";
				break;
			case 3:
				format = "yyyy/MM/dd";
				break;
			default:
				format = "yyyy/MM/dd";
		}
		SimpleDateFormat dateFormat = new SimpleDateFormat(format);
		return LOG_ROOT + "/" +dateFormat.format(date);
	}
	
	private void _checkDir(String dirPath) {
		File dir = new File(dirPath);
		if(! dir.exists() || ! dir.isDirectory()) {
			dir.mkdirs();
		}
	}
	
	private void _checkFile(String filePath) throws IOException {
		File file = new File(filePath);
		if(! file.exists() || ! file.isFile()) {
			file.createNewFile();
		}
	}
	
	public static void main(String[] args) {
		LoggerService ls = new LoggerService();
		Date date = new Date();
//		DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
//		date = df.parse("2012-04-22");
		
		Calendar cal = Calendar.getInstance();
		cal.setTime(date);
		
		//日统计
		ls.dayStatistic(date);
		
		//周统计: 判断是否是周日
		if(cal.get(Calendar.DAY_OF_WEEK) == 1) {
			ls.weekStatistic(date);
		}
		
		//月统计: 判断是否月末
		if(cal.get(Calendar.DAY_OF_MONTH) == cal.getActualMaximum(Calendar.DAY_OF_MONTH)) {
			ls.monthStatistic(date);
		}
		
		//年统计: 判断是否年末
		if(cal.get(Calendar.DAY_OF_YEAR) == cal.getActualMaximum(Calendar.DAY_OF_YEAR)) {
			ls.yearStatistic(date);
		}
	}
	
	public void addTest() {
		log("我", "add stone", LogConstrants.STONE_ADD);
		log("我", "update stone", LogConstrants.STONE_UPDATE);
		log("我", "add stone", LogConstrants.STONE_ADD);
		log("我", "add provider", LogConstrants.PROVIDER_ADD);
		//log("我", "update provider", PROVIDER_UPDATE);
		log("我", "add provider", LogConstrants.PROVIDER_ADD);
		
		log("你", "add stone", LogConstrants.STONE_ADD);
		log("你", "update stone", LogConstrants.STONE_UPDATE);
		log("你", "add stone", LogConstrants.STONE_ADD);
		log("你", "add provider", LogConstrants.PROVIDER_ADD);
		//log("你", "update provider", PROVIDER_UPDATE);
		log("你", "add provider", LogConstrants.PROVIDER_ADD);
		
		log("他", "add stone", LogConstrants.STONE_ADD);
		log("他", "update stone", LogConstrants.STONE_UPDATE);
		log("他", "add stone", LogConstrants.STONE_ADD);
		log("他", "add provider", LogConstrants.PROVIDER_ADD);
		log("他", "update provider", LogConstrants.PROVIDER_UPDATE);
		log("他", "add provider", LogConstrants.PROVIDER_ADD);
	}

}
